#include "simulationwindow.h"
#include "userdata.h"

#include "sk_game_project.h"
#include "sk_game_level.h"
#include "sk_game_shape.h"

#include <QMessageBox>
#include <QTimer>
#include <QMouseEvent>
#include <QDebug>

#include <Box2D.h>

SimulationWindow::SimulationWindow(QWidget *parent) :
    QGLWidget(parent)
{
    _UD.sw = this;
    _simProject = 0;
    _world = 0;
    _timer = new QTimer(this);
    bool isOk = connect(_timer, SIGNAL(timeout()), SLOT(onTimer()));
    Q_ASSERT(isOk);
    uint32 flags = 0;
    flags |= b2DebugDraw::e_shapeBit;
    flags |= b2DebugDraw::e_centerOfMassBit;
    _dd.SetFlags(flags);
    _isGame = false;
    _next = 0;
    setMouseTracking(true);
}

SimulationWindow::~SimulationWindow()
{
    _UD.sw = 0;
    stop();
}

void SimulationWindow::mousePressEvent(QMouseEvent * event)
{

    if (!_isGame)
    {
        return;
    }
    if (_objInd >= _level->dropShapes.size())
    {
        return;
    }
    _mPos.Set(event->pos().x() * PIXEL_TO_METERS, event->pos().y() * PIXEL_TO_METERS);
    sk::GameShapeRef s = _level->dropShapes[_objInd];
    s->fixtureDef.density = 0.0f;
    s->fixtureDef.isSensor = true;

    s->bodyDef.type = b2_dynamicBody;
    s->bodyDef.fixedRotation = true;

    s->bodyDef.position.Set(event->pos().x(), event->pos().y());
    _next = s->addToWorld(_world);

    s->fixtureDef.density = 1.0f;
    s->fixtureDef.isSensor = false;

    s->bodyDef.fixedRotation = false;


}

void SimulationWindow::mouseMoveEvent(QMouseEvent * event)
{
    _mPos.Set(event->pos().x() * PIXEL_TO_METERS, event->pos().y() * PIXEL_TO_METERS);
}

void SimulationWindow::mouseReleaseEvent(QMouseEvent * event)
{
    if (_next)
    {
        _world->DestroyBody(_next);

        if (_canPaste)
        {
            _mPos.Set(event->pos().x() * PIXEL_TO_METERS, event->pos().y() * PIXEL_TO_METERS);
            sk::GameShapeRef s = _level->dropShapes[_objInd];
            s->bodyDef.position.Set(event->pos().x(), event->pos().y());
            s->addToWorld(_world);
            ++_objInd;
        }
        _next = 0;
    }


#if 0
    if (!_isGame)
    {
        return;
    }
    if (_objInd >= _level->dropShapes.size())
    {
        return;
    }
    sk::GameShapeRef s = _level->dropShapes[_objInd];
    s->fixtureDef.density = 1.0f;
    s->bodyDef.type = b2_dynamicBody;
    s->bodyDef.position.Set(event->pos().x(), event->pos().y());
    b2Body * b = s->addToWorld(_world);
    ++_objInd;
#endif
}

void SimulationWindow::stop()
{    
    if (_simProject)
    {
        delete _simProject;
        _simProject = 0;
    }
    if (_world)
    {
        _timer->stop();
        delete _world;
        _world = 0;
        update();
    }
    _next = 0;
}

 void SimulationWindow::initializeGL()
 {
     glClearColor(1.0, 1.0, 1.0, 1.0);     
     glEnableClientState(GL_VERTEX_ARRAY);
 }

 void SimulationWindow::resizeGL(int w, int h)
 {
     glViewport(0, 0, (GLint)w, (GLint)h);


     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();

     gluOrtho2D(0, w, h, 0);

     glScalef(METER_TO_PIXELS, METER_TO_PIXELS, 1.0f);

 }

 void SimulationWindow::onTimer()
 {

     _canPaste = true;
     if (_next)
     {


         b2Vec2 p = _mPos - _next->GetPosition();
         if (_next->GetFixtureList()->GetShape()->GetType() == b2Shape::e_circle)
         {
             b2CircleShape * s = static_cast<b2CircleShape *>(_next->GetFixtureList()->GetShape());
             b2Vec2 d;
             d.Set(s->m_radius, s->m_radius);
             p += d;
         }
         p *= 8.0f;
         _next->SetLinearVelocity(p);
         /*
         for (b2ContactEdge * c = _next->GetContactList(); c; c = c->GetNext())
         {
             if (c->contact->IsTouching())
             {
                 _canPaste = false;
                 break;
             }
         }
         */
     }

     if (_canPaste)
     {
         glClearColor(1.0, 1.0f, 1.0, 1.0f);
     }
     else
     {
         glClearColor(1.0f, 0.0f, 0, 1.0f);
     }

     _world->Step(1.0f / 30.0f, 6, 2);
     if (_isGame)
     {
         for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext())
         {
             if (b->GetType() == b2_dynamicBody)
             {
                 b2Vec2 v = b->GetWorldCenter();
                 const float my = 750.0f * PIXEL_TO_METERS;                 
                 if (v.y > my)
                 {                     
                     QMessageBox::information(this, tr("Game over:("), tr("You lose..."));
                     stop();
                     _isGame = false;
                     break;
                 }
             }
         }
     }
     if (_isGame && _objInd >= _level->dropShapes.size())
     {
         _needTime -= (1.0f / 30.0f);
         if (_needTime < 0)
         {
             QMessageBox::information(this, tr("Great!"), tr("You win!"));
             stop();
             _isGame = false;
         }
     }

     update();
 }

 void SimulationWindow::paintGL()
 {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();

    if (0 == _world)
    {
        return;
    }
    _world->DrawDebugData();
 }

 void SimulationWindow::initWorld(int level)
 {
     sk::GameLevel & l = _simProject->levels[level];
     _level = &l;
     b2Vec2 g;
     g.Set(l.gx, l.gy);
    _world = new b2World(g, true);
    _world->SetAutoClearForces(true);
    for (int i = 0; i < l.shapes.size(); ++i)
    {
        l.shapes[i]->addToWorld(_world);
    }
    _world->SetDebugDraw(&_dd);
 }

 void SimulationWindow::start(int lvl, bool isGame)
 {
    if (!_UD.project->saveToDisk())
    {
        QMessageBox::critical(this, tr("Can not start simultation"), tr("Can not save sim. project.") );
        return;
    }
    stop();
    _simProject = new sk::GameProject();
    if (!_simProject->open(_UD.project->path()))
    {
        QMessageBox::critical(this, tr("Can not start simultation"), tr("Can not open sim. project.") );
        return;
    }
    _isGame = isGame;
    _objInd = 0;
    initWorld(lvl);
    _needTime = 5.0f;
    _timer->start(1000 / 30);
 }
